﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Workflows.Components;

namespace Workflows.Designer.Export
{
    public partial class ExportAsConsoleApp : Form
    {
        private WorkflowDesign _WfDesign;

        public ExportAsConsoleApp(WorkflowDesign wfDesign)
        {
            this._WfDesign = wfDesign;
            InitializeComponent();
        }

        private void ExportAsConsoleApp_Load(object sender, EventArgs e)
        {
            if(this._WfDesign !=null)
            {
                this.txt_WorkflowName.Text = this._WfDesign.WorkflowName;
                this.txt_Author.Text = this._WfDesign.Author;
                this.txt_CreationTime.Text = this._WfDesign.CreationTime.ToShortDateString();

                List<string> dependentFiles = this.GetRequiredAssembliesForWorkflow(this._WfDesign);
                this.lb_Dependencies.Items.Clear();
                if(dependentFiles !=null && dependentFiles.Count>0)
                {
                    foreach(string dependentFile in dependentFiles)
                    {
                        this.lb_Dependencies.Items.Add(dependentFile);
                    }
                }

                this.lv_Arguments.Items.Clear();
                if(this._WfDesign.GlobalVariables !=null && this._WfDesign.GlobalVariables.Length>0)
                {
                    foreach(GlobalVariable gVar in this._WfDesign.GlobalVariables)
                    {
                        ListViewItem lvItem=new ListViewItem(gVar.VarName);
                        if (gVar.VarValue != null)
                            lvItem.SubItems.Add(gVar.VarValue.ToString());
                        else
                            lvItem.SubItems.Add(string.Empty);
                        lvItem.SubItems.Add("description...");
                        this.lv_Arguments.Items.Add(lvItem);
                    }
                }
            }
        }

        private List<string> GetRequiredAssembliesForWorkflow(WorkflowDesign wfDesign)
        {
            List<string> dependentFiles=new List<string>();
            List<string> allAssemblyNames=new List<string>();

            // get all assemblies
            Dictionary<Type, string> typeAssemblies = new Dictionary<Type, string>();
            string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            DirectoryInfo dirInfo=new DirectoryInfo(binFolderPath);
            FileInfo[] fileInfos = dirInfo.GetFiles("*.dll", SearchOption.TopDirectoryOnly);
            foreach (FileInfo fileInfo in fileInfos)
            {
                try
                {
                    Assembly assembly = Assembly.LoadFrom(fileInfo.FullName);
                    allAssemblyNames.Add(assembly.GetName().Name);

                    Type[] types = assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        if (type.IsSubclassOf(typeof (WorkflowComponentBase)))
                        {
                            typeAssemblies.Add(type, fileInfo.FullName);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ExceptionHandler.HandlesException(ex);
                }
            }

            if(wfDesign.Components !=null && wfDesign.Components.Length>0)
            {
                foreach(WorkflowComponentBase wfComponent in wfDesign.Components)
                {
                    if(typeAssemblies.ContainsKey(wfComponent.GetType()))
                    {
                        string assemblyFilePath = typeAssemblies[wfComponent.GetType()];
                        if(!dependentFiles.Contains(assemblyFilePath))
                        {
                            dependentFiles.Add(assemblyFilePath);
                            Assembly assembly = Assembly.LoadFrom(assemblyFilePath);
                            this.GetDependentAssembliesForAssembly(
                                assembly, binFolderPath, allAssemblyNames, ref dependentFiles);
                        }
                    }
                }
            }

            return dependentFiles;
        }

        private void GetDependentAssembliesForAssembly(
            Assembly assembly, string binFolderPath, 
            List<string> allAssemblyNames,
            ref List<string> refAssemblyFilePaths)
        {
            AssemblyName[] refAssemNames = assembly.GetReferencedAssemblies();
            foreach (AssemblyName refAssemblyName in refAssemNames)
            {
                if (allAssemblyNames.Contains(refAssemblyName.Name))
                {
                    string assemblyFilePath = Path.Combine(binFolderPath, refAssemblyName.Name + ".dll");
                    if (!refAssemblyFilePaths.Contains(assemblyFilePath))
                    {
                        refAssemblyFilePaths.Add(assemblyFilePath);
                        Assembly nestedAssembly = Assembly.LoadFile(assemblyFilePath);
                        this.GetDependentAssembliesForAssembly(
                            nestedAssembly, binFolderPath, allAssemblyNames, ref refAssemblyFilePaths);
                    }
                }
            }
        }

        private void btn_SaveAs_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlg=new SaveFileDialog();
            dlg.Filter = "Exe files(*.exe)|*.exe|All files(*.*)|*.*";
            if(dlg.ShowDialog()==DialogResult.OK)
            {
                this.txt_ExeFileName.Text = dlg.FileName;
                string fileName = Path.GetFileNameWithoutExtension(dlg.FileName);
                this.txt_ConfigFileName.Text = dlg.FileName + ".config";
                this.txt_Argument.Text = fileName + ".settings";
            }
        }

        private void btn_Export_Click(object sender, EventArgs e)
        {
            string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string exportFolderPath = Path.Combine(binFolderPath, "Export");
            string appConfigFilePath = Path.Combine(exportFolderPath, "app.config");
            string exeFilePath = Path.Combine(exportFolderPath, "SingleWorkflowConsoleApp.exe");

            if(!File.Exists(appConfigFilePath))
            {
                MessageBox.Show("Can not find config file");
                return;
            }
            if(!File.Exists(exeFilePath))
            {
                MessageBox.Show("Can not find console exe file template");
                return;
            }
            if(string.IsNullOrEmpty(this.txt_ExeFileName.Text))
            {
                MessageBox.Show("Please specify where the exe file should be exported");
                this.txt_ExeFileName.Focus();
                return;
            }
            if(string.IsNullOrEmpty(this._WfDesign.DesignDocumentFilePath) || !File.Exists(this._WfDesign.DesignDocumentFilePath))
            {
                MessageBox.Show("Unable to find workflow design file");
                return;
            }

            string exeFileName = Path.GetFileNameWithoutExtension(this.txt_ExeFileName.Text);
            File.Copy(appConfigFilePath, this.txt_ConfigFileName.Text,true);
            File.Copy(exeFilePath, this.txt_ExeFileName.Text, true);
            string tgtFolderPath = Path.GetDirectoryName(this.txt_ExeFileName.Text);
            if(this.lb_Dependencies.Items.Count>0)
            {
                for(int i=0;i<this.lb_Dependencies.Items.Count;i++)
                {
                    string srcFilePath = this.lb_Dependencies.Items[i].ToString();
                    string tgtFilePath = Path.Combine(tgtFolderPath, Path.GetFileName(srcFilePath));
                    File.Copy(srcFilePath,tgtFilePath,true);
                }
            }

            string designFolderPath = Path.GetDirectoryName(this._WfDesign.DesignDocumentFilePath);
            string fileName = Path.GetFileNameWithoutExtension(this._WfDesign.DesignDocumentFilePath);
            string xmlFilePath = Path.Combine(designFolderPath, fileName + ".xml");
            if(!File.Exists(xmlFilePath))
            {
                MessageBox.Show("Xml setting file does not exist");
                return;
            }
            File.Copy(this._WfDesign.DesignDocumentFilePath, Path.Combine(tgtFolderPath, exeFileName + ".settings"), true);
            File.Copy(xmlFilePath, Path.Combine(tgtFolderPath, exeFileName + ".xml"), true);

            this.Close();
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
